// This code is derived from jcifs smb client library <jcifs at samba dot org>
// Ported by J. Arturo <webmaster at komodosoft dot net>
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
using System;
using SharpCifs.Util;
using SharpCifs.Util.Sharpen;

namespace SharpCifs.Dcerpc.Ndr
{
    public class NdrBuffer
    {
        internal int Referent;

        internal Hashtable Referents;

        internal class Entry
        {
            internal int Referent;

            internal object Obj;
        }

        public byte[] Buf;

        public int Start;

        public int Index;

        public int Length;

        public NdrBuffer Deferred;

        public NdrBuffer(byte[] buf, int start)
        {
            this.Buf = buf;
            this.Start = Index = start;
            Length = 0;
            Deferred = this;
        }

        public virtual NdrBuffer Derive(int idx)
        {
            NdrBuffer nb = new NdrBuffer(Buf, Start);
            nb.Index = idx;
            nb.Deferred = Deferred;
            return nb;
        }

        public virtual void Reset()
        {
            Index = Start;
            Length = 0;
            Deferred = this;
        }

        public virtual int GetIndex()
        {
            return Index;
        }

        public virtual void SetIndex(int index)
        {
            this.Index = index;
        }

        public virtual int GetCapacity()
        {
            return Buf.Length - Start;
        }

        public virtual int GetTailSpace()
        {
            return Buf.Length - Index;
        }

        public virtual byte[] GetBuffer()
        {
            return Buf;
        }

        public virtual int Align(int boundary, byte value)
        {
            int n = Align(boundary);
            int i = n;
            while (i > 0)
            {
                Buf[Index - i] = value;
                i--;
            }
            return n;
        }

        public virtual void WriteOctetArray(byte[] b, int i, int l)
        {
            Array.Copy(b, i, Buf, Index, l);
            Advance(l);
        }

        public virtual void ReadOctetArray(byte[] b, int i, int l)
        {
            Array.Copy(Buf, Index, b, i, l);
            Advance(l);
        }

        public virtual int GetLength()
        {
            return Deferred.Length;
        }

        public virtual void SetLength(int length)
        {
            Deferred.Length = length;
        }

        public virtual void Advance(int n)
        {
            Index += n;
            if ((Index - Start) > Deferred.Length)
            {
                Deferred.Length = Index - Start;
            }
        }

        public virtual int Align(int boundary)
        {
            int m = boundary - 1;
            int i = Index - Start;
            int n = ((i + m) & ~m) - i;
            Advance(n);
            return n;
        }

        public virtual void Enc_ndr_small(int s)
        {
            Buf[Index] = unchecked((byte)(s & unchecked(0xFF)));
            Advance(1);
        }

        public virtual int Dec_ndr_small()
        {
            int val = Buf[Index] & unchecked(0xFF);
            Advance(1);
            return val;
        }

        public virtual void Enc_ndr_short(int s)
        {
            Align(2);
            Encdec.Enc_uint16le((short)s, Buf, Index);
            Advance(2);
        }

        public virtual int Dec_ndr_short()
        {
            Align(2);
            int val = Encdec.Dec_uint16le(Buf, Index);
            Advance(2);
            return val;
        }

        public virtual void Enc_ndr_long(int l)
        {
            Align(4);
            Encdec.Enc_uint32le(l, Buf, Index);
            Advance(4);
        }

        public virtual int Dec_ndr_long()
        {
            Align(4);
            int val = Encdec.Dec_uint32le(Buf, Index);
            Advance(4);
            return val;
        }

        public virtual void Enc_ndr_hyper(long h)
        {
            Align(8);
            Encdec.Enc_uint64le(h, Buf, Index);
            Advance(8);
        }

        public virtual long Dec_ndr_hyper()
        {
            Align(8);
            long val = Encdec.Dec_uint64le(Buf, Index);
            Advance(8);
            return val;
        }

        public virtual void Enc_ndr_string(string s)
        {
            Align(4);
            int i = Index;
            int len = s.Length;
            Encdec.Enc_uint32le(len + 1, Buf, i);
            i += 4;
            Encdec.Enc_uint32le(0, Buf, i);
            i += 4;
            Encdec.Enc_uint32le(len + 1, Buf, i);
            i += 4;
            try
            {
                Array.Copy(Runtime.GetBytesForString(s, "UTF-16LE"), 0, Buf, i, len * 2);
            }
            catch (UnsupportedEncodingException)
            {
            }
            i += len * 2;
            Buf[i++] = unchecked((byte)('\0'));
            Buf[i++] = unchecked((byte)('\0'));
            Advance(i - Index);
        }

        /// <exception cref="SharpCifs.Dcerpc.Ndr.NdrException"></exception>
        public virtual string Dec_ndr_string()
        {
            Align(4);
            int i = Index;
            string val = null;
            int len = Encdec.Dec_uint32le(Buf, i);
            i += 12;
            if (len != 0)
            {
                len--;
                int size = len * 2;
                try
                {
                    if (size < 0 || size > unchecked(0xFFFF))
                    {
                        throw new NdrException(NdrException.InvalidConformance);
                    }
                    val = Runtime.GetStringForBytes(Buf, i, size, "UTF-16LE");
                    i += size + 2;
                }
                catch (UnsupportedEncodingException)
                {
                }
            }
            Advance(i - Index);
            return val;
        }

        private int GetDceReferent(object obj)
        {
            Entry e;
            if (Referents == null)
            {
                Referents = new Hashtable();
                Referent = 1;
            }
            if ((e = (Entry)Referents.Get(obj)) == null)
            {
                e = new Entry();
                e.Referent = Referent++;
                e.Obj = obj;
                Referents.Put(obj, e);
            }
            return e.Referent;
        }

        public virtual void Enc_ndr_referent(object obj, int type)
        {
            if (obj == null)
            {
                Enc_ndr_long(0);
                return;
            }
            switch (type)
            {
                case 1:
                case 3:
                    {
                        Enc_ndr_long(Runtime.IdentityHashCode(obj));
                        return;
                    }

                case 2:
                    {
                        Enc_ndr_long(GetDceReferent(obj));
                        return;
                    }
            }
        }

        public override string ToString()
        {
            return "start=" + Start + ",index=" + Index + ",length=" + GetLength();
        }
    }
}
